using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Documents;

namespace YouVisio.Silverlight.TextDiff
{
    public class OutputManager : IOutputManager
    {

        public OutputManager(ISettingsSource settings)
        {
            Settings = settings;
        }

        protected ISettingsSource Settings { get; private set; }


        public void GenerateOutput(IEnumerable<TextPart> parts, ITextDiffViewerUI ui)
        {

            var tupleParagraphs = ui.ResetOutputParagraphs();
            var leftBlock = tupleParagraphs.Item1;
            var rightBlock = tupleParagraphs.Item2;
            
            var linesCount = 0;
            StringBuilder sbNumbers = null;
            var hasLineNumbers = Settings.IsVisibleLeftLinesNumbersBar || Settings.IsVisibleRightLinesNumbersBar;
            if (hasLineNumbers) sbNumbers = new StringBuilder();

            var blocksList = new List<BlockInfo>();

            foreach (var textPart in parts)
            {
                var firstLine = linesCount + 1;
                foreach (var pair in textPart.Enumerate())
                {
                    linesCount++;
                    if (hasLineNumbers)
                    {
                        sbNumbers.AppendLine(linesCount.ToString());
                    }
                    if (textPart.Operation != Operation.Delete)
                    {
                        rightBlock.Inlines.Add(textPart.AddChangedInline(new Run { Text = pair.Value }));
                    }
                    if (textPart.Operation != Operation.Insert)
                    {
                        leftBlock.Inlines.Add(textPart.AddInitialInline(new Run { Text = pair.Key }));
                    }
                    rightBlock.Inlines.Add(textPart.AddChangedInline(new LineBreak()));
                    leftBlock.Inlines.Add(textPart.AddInitialInline(new LineBreak()));
                }
                var lastLine = linesCount;
                if (textPart.OperationIsModification())
                {
                    blocksList.Add(new BlockInfo { Operation = textPart.Operation, FirstLine = firstLine, LastLine = lastLine });
                }
            }

            // remove th last breaks
            if (rightBlock.Inlines.Count > 0) rightBlock.Inlines.RemoveAt(rightBlock.Inlines.Count - 1);
            if (leftBlock.Inlines.Count > 0) leftBlock.Inlines.RemoveAt(leftBlock.Inlines.Count - 1);

            ui.OutputParagraphsPopulated();

            ui.GenerateLineNumbers(hasLineNumbers, sbNumbers == null ? "" : sbNumbers.ToString(), linesCount);

            ui.GenerateColoredBackgrounds(blocksList, linesCount);

            ui.GenerateLinesPreview(blocksList, linesCount);

        }

        public void PopulateChangedWordsInlines(Paragraph leftParagraph,
                                         Paragraph rightParagraph,
                                         IEnumerable<Word> leftWords,
                                         IEnumerable<Word> rightWords,
                                         Tuple<TextPart, TextPart> initialParts)
        {
            if (initialParts.Item1 == null) return;
            var index = RemoveInlinesAndGetIndex(initialParts, leftParagraph, t => t.Item1.InitialInlines, t => t.Item2.InitialInlines);
            if (index != -1 && leftParagraph != null)
            {
                index = WordsToInlines(leftWords).Aggregate(index, (i, inline) =>
                {
                    leftParagraph.Inlines.Insert(i++, inline);
                    return i;
                });
                if(initialParts.Item2 != null && initialParts.Item2.Operation == Operation.Insert)
                {
                    foreach (var s in initialParts.Item2.Changed)
                        leftParagraph.Inlines.Insert(index++, new LineBreak());
                }
            }
            index = RemoveInlinesAndGetIndex(initialParts, rightParagraph, t => t.Item1.ChangedInlines, t => t.Item2.ChangedInlines);
            if (index != -1 && rightParagraph != null)
            {
                index = WordsToInlines(rightWords).Aggregate(index, (i, inline) =>
                {
                    rightParagraph.Inlines.Insert(i++, inline);
                    return i;
                });
                if (initialParts.Item2 != null && initialParts.Item2.Operation == Operation.Delete)
                {
                    foreach (var s in initialParts.Item2.Initial)
                        rightParagraph.Inlines.Insert(index++, new LineBreak());
                }
            }
        }

        private static List<Inline> WordsToInlines(IEnumerable<Word> words)
        {
            return words.Aggregate(new List<Inline>(), (l, w) =>
            {
                l.Add(w.IsBreak
                       ? (Inline)new LineBreak()
                       : w.IsChanged
                           ? new Run { Text = w.Text, FontWeight = FontWeights.Bold }
                           : new Run { Text = w.Text }
                    );
                return l;
            });
        }

        private static int RemoveInlinesAndGetIndex(Tuple<TextPart, TextPart> parts, Paragraph paragraph, Func<Tuple<TextPart, TextPart>, List<Inline>> getInlines1, Func<Tuple<TextPart, TextPart>, List<Inline>> getInlines2)
        {
            if (paragraph != null && getInlines1(parts).Count > 0)
            {
                var first = getInlines1(parts)[0];
                var index = paragraph.Inlines.IndexOf(first);
                getInlines1(parts).Aggregate(paragraph, (p, i) => { p.Inlines.Remove(i); return p; });
                if (parts.Item2 != null) getInlines2(parts).Aggregate(paragraph, (p, i) => { p.Inlines.Remove(i); return p; });
                return index;
            }
            return -1;
        }

        




        





        
    }
}
